#include "netclient.h"

#include <QNetworkReply>

#include "../utils/log.hpp"
#include "../model/datacenter.h"
#include "gateway.qpb.h"
#include "user.qpb.h"
#include "friend.qpb.h"
#include "message_storage.qpb.h"
#include "message_transmit.qpb.h"
#include "notify.qpb.h"
#include "file.qpb.h"
#include "speech_recognition.qpb.h"

network::NetClient::NetClient(model::DataCenter* dataCenter) : dataCenter(dataCenter) {}

/* ===========================================
 *            Http请求响应处理
 * ========================================== */

void network::NetClient::ping()
{
    QNetworkRequest httpReq;
    httpReq.setUrl(QUrl(HTTP_URL + "/ping"));

    QNetworkReply* httpResp = httpClient.get(httpReq);
    connect(httpResp, &QNetworkReply::finished, this, [=](){
        if(httpResp->error() != QNetworkReply::NoError) {
            LOG(LL_WARN, "HTTP 请求失败: {}", httpResp->errorString());
            return;
        }
        QByteArray body = httpResp->readAll();
        LOG(LL_DEBUG, "响应内容: {}", body);
        httpResp->deleteLater();
    });
}

void network::NetClient::getSelfInfo(const QString& loginSessionId)
{
    // 1.构造http请求中的body
    im_proto::GetUserInfoReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    QByteArray body = req.serialize(&serializer);
    LOG(LL_DEBUG, "[获取个人信息--发送请求] requestId: {}, loginSessionId: {}", req.requestId(), req.sessionId());

    // 2.发送异步请求
    QNetworkReply* httpResp = sendHttpRequest("/service/user/get_user_info", body);

    connect(httpResp, &QNetworkReply::finished, this, [=](){
        // 3-1 获取个人信息响应
        bool ok = false;
        QString reason;
        std::shared_ptr<im_proto::GetUserInfoRsp> userInfoResp = httpResponseHandle<im_proto::GetUserInfoRsp>(httpResp, &ok, &reason);
        if(!ok) {
            LOG(LL_ERROR, "获取个人信息有误: reson: {}", reason);
            return;
        }

        // 3-2 将响应数据保存在数据处理器中
        dataCenter->resetSelfInfo(userInfoResp);

        // 3-3 通知调用执行流, 告知其
        emit dataCenter->getSelfInfoDone();

        LOG(LL_DEBUG, "[获取个人信息--处理响应] requestId: {}", req.requestId());
    });
}

void network::NetClient::getFriendList(const QString& loginSessionId)
{
    im_proto::GetFriendListReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    QByteArray body = req.serialize(&serializer);
    LOG(LL_DEBUG, "[获取好友列表--发送请求] requestId: {}, loginSessionId: {}", req.requestId(), req.sessionId());

    QNetworkReply* httpResp = sendHttpRequest("/service/friend/get_friend_list", body);

    connect(httpResp, &QNetworkReply::finished, this, [=](){
        bool ok = false;
        QString reason;
        std::shared_ptr<im_proto::GetFriendListRsp> friendListResp = httpResponseHandle<im_proto::GetFriendListRsp>(httpResp, &ok, &reason);
        if(!ok) {
            LOG(LL_ERROR, "获取好友列表有误: reson: {}", reason);
            return;
        }

        dataCenter->resetFriendList(friendListResp);

        emit dataCenter->getFriendListDone();

        LOG(LL_DEBUG, "[获取好友列表--处理响应] requestId: {}", req.requestId());
    });
}

void network::NetClient::getChatSessionList(const QString& loginSessionId)
{
    im_proto::GetChatSessionListReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    QByteArray body = req.serialize(&serializer);
    LOG(LL_DEBUG, "[获取会话列表--发送请求] requestId: {}, loginSessionId: {}", req.requestId(), req.sessionId());

    QNetworkReply* httpResp = sendHttpRequest("/service/friend/get_chat_session_list", body);

    connect(httpResp, &QNetworkReply::finished, this, [=](){
        bool ok = false;
        QString reason;
        std::shared_ptr<im_proto::GetChatSessionListRsp> chatSessionListResp = httpResponseHandle<im_proto::GetChatSessionListRsp>(httpResp, &ok, &reason);
        if(!ok) {
            LOG(LL_ERROR, "获取会话列表有误: reson: {}", reason);
            return;
        }

        dataCenter->resetChatSessionList(chatSessionListResp);

        emit dataCenter->getChatSessionListDone();

        LOG(LL_DEBUG, "[获取会话列表--处理响应] requestId: {}", req.requestId());
    });
}

void network::NetClient::getApplyList(const QString& loginSessionId)
{
    im_proto::GetPendingFriendEventListReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    QByteArray body = req.serialize(&serializer);
    LOG(LL_DEBUG, "[获取好友申请列表--发送请求] requestId: {}, loginSessionId: {}", req.requestId(), req.sessionId());

    QNetworkReply* httpResp = sendHttpRequest("/service/friend/get_pending_friend_events", body);

    connect(httpResp, &QNetworkReply::finished, this, [=](){
        bool ok = false;
        QString reason;
        std::shared_ptr<im_proto::GetPendingFriendEventListRsp> applyListResp = httpResponseHandle<im_proto::GetPendingFriendEventListRsp>(httpResp, &ok, &reason);
        if(!ok) {
            LOG(LL_ERROR, "获取好友申请列表有误: reson: {}", reason);
            return;
        }

        dataCenter->resetApplyList(applyListResp);

        emit dataCenter->getApplyListDone();

        LOG(LL_DEBUG, "[获取好友申请列表--处理响应] requestId: {}", req.requestId());
    });
}

void network::NetClient::getRecentMessageList(const QString &loginSessionId, const QString &chatSessionId)
{
    im_proto::GetRecentMsgReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    req.setChatSessionId(chatSessionId);
    req.setMsgCount(50);
    QByteArray body = req.serialize(&serializer);
    LOG(LL_DEBUG, "[获取消息会话--发送请求] requestId: {}, chatSessionId: {}", req.requestId(), chatSessionId);

    QNetworkReply* httpResp = sendHttpRequest("/service/message_storage/get_recent", body);

    connect(httpResp, &QNetworkReply::finished, this, [=](){
        bool ok = false;
        QString reason;
        std::shared_ptr<im_proto::GetRecentMsgRsp> recentMsgListResp = httpResponseHandle<im_proto::GetRecentMsgRsp>(httpResp, &ok, &reason);
        if(!ok) {
            LOG(LL_ERROR, "获取消息会话有误: reson: {}", reason);
            return;
        }

        dataCenter->resetRecentMessageList(chatSessionId, recentMsgListResp);

        emit dataCenter->getRecentMessageListDone(chatSessionId);

        LOG(LL_DEBUG, "[获取消息会话--处理响应] requestId: {}", req.requestId());
    });
}

void network::NetClient::sendMessage(const QString& loginSessionId, const QString& chatSessionId,
                                     model::MessageType messageType, const QByteArray& content, const QString& extraInfo)
{
    // 1. 构造请求数据
    im_proto::NewMessageReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    req.setChatSessionId(chatSessionId);

    im_proto::MessageContent messageContent;
    if(messageType == model::MessageType::TEXT_TYPE) {
        messageContent.setMessageType(im_proto::MessageTypeGadget::STRING);
        im_proto::StringMessageInfo stringMessageInfo;
        stringMessageInfo.setContent(content);
        messageContent.setStringMessage(stringMessageInfo);
    } else if(messageType == model::MessageType::IMAGE_TYPE) {
        messageContent.setMessageType(im_proto::MessageTypeGadget::IMAGE);
        im_proto::ImageMessageInfo imageMessageInfo;
        imageMessageInfo.setFileId("");
        imageMessageInfo.setImageContent(content);
        messageContent.setImageMessage(imageMessageInfo);
    } else if(messageType == model::MessageType::FILE_TYPE) {
        messageContent.setMessageType(im_proto::MessageTypeGadget::FILE);
        im_proto::FileMessageInfo fileMessageInfo;
        fileMessageInfo.setFileId("");
        fileMessageInfo.setFileSize(0);
        fileMessageInfo.setFileName(extraInfo);
        fileMessageInfo.setFileContents(content);
        messageContent.setFileMessage(fileMessageInfo);
    } else if(messageType == model::MessageType::SPEECH_TYPE) {
        messageContent.setMessageType(im_proto::MessageTypeGadget::SPEECH);
        im_proto::SpeechMessageInfo speechMessageInfo;
        speechMessageInfo.setFileId("");
        speechMessageInfo.setFileContents(content);
        messageContent.setSpeechMessage(speechMessageInfo);
    }
    req.setMessage(messageContent);

    QByteArray body = req.serialize(&serializer);
    LOG(LL_DEBUG, "[发送消息--请求] requestId: {}, loginSessionId: {}, chatSessionId: {}, messageType: {}",
        req.requestId(), loginSessionId, chatSessionId, messageType);

    // 2.发送请求
    QNetworkReply* httpResp = sendHttpRequest("/service/message_transmit/new_message", body);

    // 3.响应处理
    connect(httpResp, &QNetworkReply::finished, this, [=](){
        bool ok = false;
        QString reason;
        std::shared_ptr<im_proto::NewMessageRsp> newMessageResp = httpResponseHandle<im_proto::NewMessageRsp>(httpResp, &ok, &reason);
        if(!ok) {
            LOG(LL_ERROR, "获取消息会话有误: reson: {}", reason);
            return;
        }

        emit dataCenter->sendMessageDone(messageType, content, extraInfo);

        LOG(LL_DEBUG, "[发送消息--响应] requestId: {}", req.requestId());
    });
}

void network::NetClient::changeNicknameAsync(const QString& loginSessionId, const QString& nickname)
{
    im_proto::SetUserNicknameReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    req.setNickname(nickname);
    QByteArray body = req.serialize(&serializer);
    LOG(LL_DEBUG, "[修改用户昵称::发送请求] requestId: {}, loginSessionId: {}, nickname: {}", req.requestId(), loginSessionId, nickname);

    QNetworkReply* httpResp = sendHttpRequest("/service/user/set_nickname", body);

    connect(httpResp, &QNetworkReply::finished, this, [=](){
        bool ok = false;
        QString reason;
        std::shared_ptr<im_proto::SetUserNicknameRsp> modifyNicknameResp = httpResponseHandle<im_proto::SetUserNicknameRsp>(httpResp, &ok, &reason);
        if(!ok) {
            LOG(LL_ERROR, "修改用户昵称有误: {}", reason);
            return;
        }

        dataCenter->resetNickname(nickname);

        emit dataCenter->changeNicknameDone();

        LOG(LL_DEBUG, "[修改用户昵称::处理响应] requestId: {}", modifyNicknameResp->requestId());
    });
}

void network::NetClient::changeDescriptionAsync(const QString& loginSessionId, const QString& description)
{
    im_proto::SetUserDescriptionReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    req.setDescription(description);
    QByteArray body = req.serialize(&serializer);
    LOG(LL_DEBUG, "[修改用户签名::发送请求] requestId: {}, loginSessionId: {}, description: {}", req.requestId(), loginSessionId, description);

    QNetworkReply* httpResp = sendHttpRequest("/service/user/set_description", body);

    connect(httpResp, &QNetworkReply::finished, this, [=](){
        bool ok = false;
        QString reason;
        std::shared_ptr<im_proto::SetUserDescriptionRsp> modifyDescResp = httpResponseHandle<im_proto::SetUserDescriptionRsp>(httpResp, &ok, &reason);
        if(!ok) {
            LOG(LL_ERROR, "修改用户签名有误: {}", reason);
            return;
        }

        dataCenter->resetDescription(description);

        emit dataCenter->changeDescriptionDone();

        LOG(LL_DEBUG, "[修改用户签名::处理响应] requestId: {}", modifyDescResp->requestId());
    });
}

void network::NetClient::changePhone(const QString& loginSessionId, const QString& phone, const QString& verifyCodeId, const QString& verifyCode)
{
    im_proto::SetUserPhoneNumberReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    req.setPhoneNumber(phone);
    req.setPhoneVerifyCodeId(verifyCodeId);
    req.setPhoneVerifyCode(verifyCode);
    QByteArray body = req.serialize(&serializer);
    LOG(LL_DEBUG, "[修改用户手机号::发送请求] requestId: {}, loginSessionId: {}, phone: {}", req.requestId(), loginSessionId, phone);

    QNetworkReply* httpResp = sendHttpRequest("/service/user/set_phone", body);

    connect(httpResp, &QNetworkReply::finished, this, [=](){
        bool ok = false;
        QString reason;
        std::shared_ptr<im_proto::SetUserPhoneNumberRsp> modifyPhoneResp = httpResponseHandle<im_proto::SetUserPhoneNumberRsp>(httpResp, &ok, &reason);
        if(!ok) {
            LOG(LL_ERROR, "修改用户手机号有误: {}", reason);
            return;
        }

        dataCenter->resetPhone(phone);

        emit dataCenter->changePhoneDone();

        LOG(LL_DEBUG, "[修改用户手机号::处理响应] requestId: {}", modifyPhoneResp->requestId());
    });
}

void network::NetClient::getVerifyCode(const QString& phone)
{
    im_proto::PhoneVerifyCodeReq req;
    req.setRequestId(makeRequestId());
    req.setPhoneNumber(phone);
    QByteArray body = req.serialize(&serializer);
    LOG(LL_DEBUG, "[获取短信验证码::发送请求] requestId: {}, phone: {}", req.requestId(), phone);

    QNetworkReply* httpResp = sendHttpRequest("/service/user/get_phone_verify_code", body);

    connect(httpResp, &QNetworkReply::finished, this, [=](){
        bool ok = false;
        QString reason;
        std::shared_ptr<im_proto::PhoneVerifyCodeRsp> verifyCodeResp = httpResponseHandle<im_proto::PhoneVerifyCodeRsp>(httpResp, &ok, &reason);
        if(!ok) {
            LOG(LL_ERROR, "获取短信验证码有误: {}", reason);
            return;
        }

        dataCenter->resetVerifyCodeId(verifyCodeResp->verifyCodeId());

        emit dataCenter->getVerifyCodeDone();

        LOG(LL_DEBUG, "[获取短信验证码::处理响应] requestId: {}", verifyCodeResp->requestId());
    });
}

void network::NetClient::changeAvatar(const QString& loginSessionId, const QByteArray& imageBytes)
{
    im_proto::SetUserAvatarReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    req.setAvatar(imageBytes);
    QByteArray body = req.serialize(&serializer);
    LOG(LL_DEBUG, "[修改用户头像::发送请求] requestId: {}, loginSessionId: {}", req.requestId(), loginSessionId);

    QNetworkReply* httpResp = sendHttpRequest("/service/user/set_avatar", body);

    connect(httpResp, &QNetworkReply::finished, this, [=](){
        bool ok = false;
        QString reason;
        std::shared_ptr<im_proto::SetUserAvatarRsp> modifyAvatarResp = httpResponseHandle<im_proto::SetUserAvatarRsp>(httpResp, &ok, &reason);
        if(!ok) {
            LOG(LL_ERROR, "修改用户头像有误: {}", reason);
            return;
        }

        dataCenter->resetAvatar(imageBytes);

        emit dataCenter->changeAvatarDone();

        LOG(LL_DEBUG, "[修改用户头像::处理响应] requestId: {}", modifyAvatarResp->requestId());
    });
}

void network::NetClient::deleteFriend(const QString& loginSessionId, const QString& userId)
{
    im_proto::FriendRemoveReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    req.setPeerId(userId);
    QByteArray body = req.serialize(&serializer);
    LOG(LL_DEBUG, "[删除好友::发送请求] requestId: {}, loginSessionId: {}", req.requestId(), loginSessionId);

    QNetworkReply* httpResp = sendHttpRequest("/service/friend/remove_friend", body);

    connect(httpResp, &QNetworkReply::finished, this, [=](){
        bool ok = false;
        QString reason;
        std::shared_ptr<im_proto::FriendRemoveRsp> deleteFriendResp = httpResponseHandle<im_proto::FriendRemoveRsp>(httpResp, &ok, &reason);
        if(!ok) {
            LOG(LL_ERROR, "删除好友有误: {}", reason);
            return;
        }

        dataCenter->removeFriend(userId);

        emit dataCenter->deleteFriendDone();

        LOG(LL_DEBUG, "[删除好友::处理响应] requestId: {}", deleteFriendResp->requestId());
    });
}

void network::NetClient::addFriendApply(const QString &loginSessionId, const QString &userId)
{
    im_proto::FriendAddReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    req.setRespondentId(userId);    // 设置被申请人的id
    QByteArray body = req.serialize(&serializer);
    LOG(LL_DEBUG, "[添加好友申请::发送请求] requestId: {}, loginSessionId: {}", req.requestId(), loginSessionId);

    QNetworkReply* httpResp = sendHttpRequest("/service/friend/add_friend_apply", body);

    connect(httpResp, &QNetworkReply::finished, this, [=](){
        bool ok = false;
        QString reason;
        std::shared_ptr<im_proto::FriendAddRsp> addFriendResp = httpResponseHandle<im_proto::FriendAddRsp>(httpResp, &ok, &reason);
        if(!ok) {
            LOG(LL_ERROR, "添加好友申请有误: {}", reason);
            return;
        }

        // 批注: 改请求只是申请, 因此不需要进行数据操作, 待申请被同意后再添加数据

        emit dataCenter->addFriendApplyDone();

        LOG(LL_DEBUG, "[添加好友申请::处理响应] requestId: {}", addFriendResp->requestId());
    });
}

void network::NetClient::friendApplyProcess(const QString& loginSessionId, const QString& userId, bool agree)
{
    im_proto::FriendAddProcessReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    req.setAgree(agree);
    req.setApplyUserId(userId);
    QByteArray body = req.serialize(&serializer);

    const QString logType = agree ? "同意好友申请" : "拒绝好友申请";

    LOG(LL_DEBUG, "[{}::发送请求] requestId: {}, loginSessionId: {}", logType, req.requestId(), loginSessionId);

    QNetworkReply* httpResp = sendHttpRequest("/service/friend/add_friend_process", body);

    connect(httpResp, &QNetworkReply::finished, this, [=](){
        bool ok = false;
        QString reason;
        std::shared_ptr<im_proto::FriendAddProcessRsp> friendAddProcessResp = httpResponseHandle<im_proto::FriendAddProcessRsp>(httpResp, &ok, &reason);
        if(!ok) {
            LOG(LL_ERROR, "{}有误: {}", logType, reason);
            return;
        }

        if(agree) {
            dataCenter->acceptFriendHandle(userId);
        } else {
            dataCenter->removeFromApplyList(userId);
        }

        emit dataCenter->friendApplyProcessDone(agree);

        LOG(LL_DEBUG, "[{}::处理响应] requestId: {}", logType, friendAddProcessResp->requestId());
    });
}

void network::NetClient::createGroupChatSession(const QString& loginSessionId, const QList<QString>& userIdList)
{
    im_proto::ChatSessionCreateReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    req.setChatSessionName("新的群聊");
    req.setMemberIdList(userIdList);
    QByteArray body = req.serialize(&serializer);

    LOG(LL_DEBUG, "[创建群聊会话::发送请求] requestId: {}, loginSessionId: {}, sessionName: {}, memberCount: {}",
        req.requestId(), loginSessionId, req.chatSessionName(), userIdList.size());

    QNetworkReply* httpResp = sendHttpRequest("/service/friend/create_chat_session", body);

    connect(httpResp, &QNetworkReply::finished, this, [=](){
        bool ok = false;
        QString reason;
        std::shared_ptr<im_proto::ChatSessionCreateRsp> chatSessionCreateResp = httpResponseHandle<im_proto::ChatSessionCreateRsp>(httpResp, &ok, &reason);
        if(!ok) {
            LOG(LL_ERROR, "创建群聊会话有误: {}", reason);
            return;
        }

        // 批注: 这里只是向服务器发送创建会话的请求, 而该新会话的创建由服务器完成/推送(ws)

        emit dataCenter->createGroupChatSessionDone();

        LOG(LL_DEBUG, "[创建群聊会话::处理响应] requestId: {}", chatSessionCreateResp->requestId());
    });
}

void network::NetClient::getMemberList(const QString& loginSessionId, const QString& chatSessionId)
{
    im_proto::GetChatSessionMemberReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    req.setChatSessionId(chatSessionId);
    QByteArray body = req.serialize(&serializer);

    LOG(LL_DEBUG, "[获取会话成员列表::发送请求] requestId: {}, loginSessionId: {}, chatSessionId: {}",
        req.requestId(), loginSessionId, req.chatSessionId());

    QNetworkReply* httpResp = sendHttpRequest("/service/friend/get_chat_session_member", body);

    connect(httpResp, &QNetworkReply::finished, this, [=](){
        bool ok = false;
        QString reason;
        std::shared_ptr<im_proto::GetChatSessionMemberRsp> chatSessionMemberResp = httpResponseHandle<im_proto::GetChatSessionMemberRsp>(httpResp, &ok, &reason);
        if(!ok) {
            LOG(LL_ERROR, "获取会话成员列表有误: {}", reason);
            return;
        }

        dataCenter->resetSessionMemberList(chatSessionId, chatSessionMemberResp->memberInfoList());

        emit dataCenter->getMemberListDone(chatSessionId);

        LOG(LL_DEBUG, "[获取会话成员列表::处理响应] requestId: {}", chatSessionMemberResp->requestId());
    });
}

void network::NetClient::searchUser(const QString& loginSessionId, const QString& key)
{
    im_proto::FriendSearchReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    req.setSearchKey(key);
    QByteArray body = req.serialize(&serializer);

    LOG(LL_DEBUG, "[搜索用户::发送请求] requestId: {}, loginSessionId: {}, searchKey: {}",
        req.requestId(), loginSessionId, req.searchKey());

    QNetworkReply* httpResp = sendHttpRequest("/service/friend/search_friend", body);

    connect(httpResp, &QNetworkReply::finished, this, [=](){
        bool ok = false;
        QString reason;
        std::shared_ptr<im_proto::FriendSearchRsp> userSearchResp = httpResponseHandle<im_proto::FriendSearchRsp>(httpResp, &ok, &reason);
        if(!ok) {
            LOG(LL_ERROR, "搜索用户有误: {}", reason);
            return;
        }

        dataCenter->resetSearchUserResult(userSearchResp->userInfo());

        emit dataCenter->searchUserDone();

        LOG(LL_DEBUG, "[搜索用户::处理响应] requestId: {}", userSearchResp->requestId());
    });
}

void network::NetClient::searchMessage(const QString& loginSessionId, const QString& chatSessionId, const QString& key)
{
    im_proto::MsgSearchReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    req.setChatSessionId(chatSessionId);
    req.setSearchKey(key);
    QByteArray body = req.serialize(&serializer);

    LOG(LL_DEBUG, "[按关键词搜索历史消息::发送请求] requestId: {}, loginSessionId: {}, chatSessionId: {}, searchKey: {}",
        req.requestId(), loginSessionId, chatSessionId, req.searchKey());

    QNetworkReply* httpResp = sendHttpRequest("/service/message_storage/search_history", body);

    connect(httpResp, &QNetworkReply::finished, this, [=](){
        bool ok = false;
        QString reason;
        std::shared_ptr<im_proto::MsgSearchRsp> messageSearchResp = httpResponseHandle<im_proto::MsgSearchRsp>(httpResp, &ok, &reason);
        if(!ok) {
            LOG(LL_ERROR, "按关键词搜索历史消息有误: {}", reason);
            return;
        }

        dataCenter->resetSearchMessageResult(messageSearchResp->msgList());

        emit dataCenter->searchMessageDone();

        LOG(LL_DEBUG, "[按关键词搜索历史消息::处理响应] requestId: {}", messageSearchResp->requestId());
    });
}

void network::NetClient::searchMessageByTime(const QString& loginSessionId, const QString& chatSessionId, const QDateTime& beginTime, const QDateTime& endTime)
{
    im_proto::GetHistoryMsgReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    req.setChatSessionId(chatSessionId);
    req.setStartTime(beginTime.toSecsSinceEpoch());
    req.setOverTime(endTime.toSecsSinceEpoch());
    QByteArray body = req.serialize(&serializer);

    LOG(LL_DEBUG, "[按时间搜索历史消息::发送请求] requestId: {}, loginSessionId: {}, chatSessionId: {}, beginTime: {}, endTime: {}",
        req.requestId(), loginSessionId, chatSessionId, req.startTime(), req.overTime());

    QNetworkReply* httpResp = sendHttpRequest("/service/message_storage/get_history", body);

    connect(httpResp, &QNetworkReply::finished, this, [=](){
        bool ok = false;
        QString reason;
        std::shared_ptr<im_proto::GetHistoryMsgRsp> messageSearchByTimeResp = httpResponseHandle<im_proto::GetHistoryMsgRsp>(httpResp, &ok, &reason);
        if(!ok) {
            LOG(LL_ERROR, "按时间搜索历史消息有误: {}", reason);
            return;
        }

        dataCenter->resetSearchMessageResult(messageSearchByTimeResp->msgList());

        emit dataCenter->searchMessageDone();

        LOG(LL_DEBUG, "[按时间搜索历史消息::处理响应] requestId: {}", messageSearchByTimeResp->requestId());
    });
}

void network::NetClient::AccountLogin(const QString& username, const QString& password)
{
    im_proto::AccountLoginReq req;
    req.setRequestId(makeRequestId());
    req.setNickname(username);
    req.setPassword(password);
    req.setVerifyCodeId("");
    req.setVerifyCode("");
    QByteArray body = req.serialize(&serializer);

    LOG(LL_DEBUG, "[用户名登录::发送请求] requestId: {}, username: {}, password: {}", req.requestId(), username, password);

    QNetworkReply* httpResp = sendHttpRequest("/service/user/username_login", body);

    connect(httpResp, &QNetworkReply::finished, this, [=](){
        bool ok = false;
        QString reason;
        std::shared_ptr<im_proto::AccountLoginRsp> userLoginResp = httpResponseHandle<im_proto::AccountLoginRsp>(httpResp, &ok, &reason);
        if(!ok) {
            LOG(LL_ERROR, "用户名登录有误: {}", reason);
            emit dataCenter->loginDone(ok, reason);
            return;
        }

        dataCenter->resetLoginSessionId(userLoginResp->loginSessionId());

        emit dataCenter->loginDone(ok, reason);

        LOG(LL_DEBUG, "[用户名登录::处理响应] requestId: {}", userLoginResp->requestId());
    });
}

void network::NetClient::phoneLogin(const QString& phone, const QString& verifyCodeId, const QString& verifyCode)
{
    im_proto::PhoneLoginReq req;
    req.setRequestId(makeRequestId());
    req.setPhoneNumber(phone);
    req.setVerifyCodeId(verifyCodeId);
    req.setVerifyCode(verifyCode);
    QByteArray body = req.serialize(&serializer);

    LOG(LL_DEBUG, "[手机号登录::发送请求] requestId: {}, phone: {}, verifyCodeId: {}, verifyCode: {}", req.requestId(), phone, verifyCodeId, verifyCode);

    QNetworkReply* httpResp = sendHttpRequest("/service/user/phone_login", body);

    connect(httpResp, &QNetworkReply::finished, this, [=](){
        bool ok = false;
        QString reason;
        std::shared_ptr<im_proto::PhoneLoginRsp> phoneLoginResp = httpResponseHandle<im_proto::PhoneLoginRsp>(httpResp, &ok, &reason);
        if(!ok) {
            LOG(LL_ERROR, "手机号登录有误: {}", reason);
            emit dataCenter->loginDone(ok, reason);
            return;
        }

        dataCenter->resetLoginSessionId(phoneLoginResp->loginSessionId());

        emit dataCenter->loginDone(ok, reason);

        LOG(LL_DEBUG, "[手机号登录::处理响应] requestId: {}", phoneLoginResp->requestId());
    });
}

void network::NetClient::userRegister(const QString& username, const QString& password, const QString& phone, const QString& verifyCodeId, const QString& verifyCode)
{
    im_proto::UserRegisterReq req;
    req.setRequestId(makeRequestId());
    req.setNickname(username);
    req.setPassword(password);
    req.setPhoneNumber(phone);
    req.setVerifyCodeId(verifyCodeId);
    req.setVerifyCode(verifyCode);
    QByteArray body = req.serialize(&serializer);

    LOG(LL_DEBUG, "[用户注册::发送请求] requestId: {}, username: {}, password: {}, phone: {}, verifyCodeId: {}, verifyCode: {}",
        req.requestId(), username, password, phone, verifyCodeId, verifyCode);

    QNetworkReply* httpResp = sendHttpRequest("/service/user/register", body);

    connect(httpResp, &QNetworkReply::finished, this, [=](){
        bool ok = false;
        QString reason;
        std::shared_ptr<im_proto::UserRegisterRsp> userRegisterResp = httpResponseHandle<im_proto::UserRegisterRsp>(httpResp, &ok, &reason);
        if(!ok) {
            LOG(LL_ERROR, "用户注册有误: {}", reason);
            emit dataCenter->userRegisterDone(ok ,reason);
            return;
        }

        emit dataCenter->userRegisterDone(ok, reason);

        LOG(LL_DEBUG, "[用户注册::处理响应] requestId: {}", userRegisterResp->requestId());
    });
}

void network::NetClient::getSingleFile(const QString& loginSessionId, const QString& fileId)
{
    im_proto::GetSingleFileReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    req.setFileId(fileId);
    QByteArray body = req.serialize(&serializer);

    LOG(LL_DEBUG, "[获取文件内容::发送请求] requestId: {}, loginSessionId: {}, fileId: {}",
        req.requestId(), loginSessionId, fileId);

    QNetworkReply* httpResp = sendHttpRequest("/service/file/get_single_file", body);

    connect(httpResp, &QNetworkReply::finished, this, [=](){
        bool ok = false;
        QString reason;
        std::shared_ptr<im_proto::GetSingleFileRsp> getSingleFileResp = httpResponseHandle<im_proto::GetSingleFileRsp>(httpResp, &ok, &reason);
        if(!ok) {
            LOG(LL_ERROR, "获取文件内容有误: {}", reason);
            emit dataCenter->userRegisterDone(ok ,reason);
            return;
        }

        // 文件数据响应不保存在 dataCenter, 直接通过信号传递给调用者

        emit dataCenter->getSingleFileDone(fileId, getSingleFileResp->fileData().fileContent());

        LOG(LL_DEBUG, "[获取文件内容::处理响应] requestId: {}", getSingleFileResp->requestId());
    });
}

void network::NetClient::speechConvertText(const QString& loginSessionId, const QString& fileId, const QByteArray& content)
{
    im_proto::SpeechRecognitionReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    req.setSpeechContent(content);
    QByteArray body = req.serialize(&serializer);

    LOG(LL_DEBUG, "[语音转文字::发送请求] requestId: {}, loginSessionId: {}", req.requestId(), loginSessionId, fileId);

    QNetworkReply* httpResp = sendHttpRequest("/service/speech/recognition", body);

    connect(httpResp, &QNetworkReply::finished, this, [=](){
        bool ok = false;
        QString reason;
        std::shared_ptr<im_proto::SpeechRecognitionRsp> speechRecognitionResp = httpResponseHandle<im_proto::SpeechRecognitionRsp>(httpResp, &ok, &reason);
        if(!ok) {
            LOG(LL_ERROR, "语音转文字有误: {}", reason);
            emit dataCenter->userRegisterDone(ok ,reason);
            return;
        }

        // 转换的文字结果直接通过信号传递出去, 不需要做数据保存
        emit dataCenter->speechConvertTextDone(fileId, speechRecognitionResp->recognitionResult());

        LOG(LL_DEBUG, "[语音转文字::处理响应] requestId: {}", speechRecognitionResp->requestId());
    });
}

void network::NetClient::sendAuthentication()
{
    if(!websocketClient.isValid()) {
        LOG(LL_FATAL, "当前 websocket 无效!");
        return;
    }
    im_proto::ClientAuthenticationReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(dataCenter->getLoginSessionId());
    QByteArray body = req.serialize(&serializer);
    websocketClient.sendBinaryMessage(body);
    LOG(LL_DEBUG,"[WS身份验证] requestId: {}, loginSessionId: {}", req.requestId(), req.sessionId());
}

QNetworkReply *network::NetClient::sendHttpRequest(const QString &apiPath, const QByteArray &body)
{
    // 1.构造http请求头
    QNetworkRequest httpReq;
    httpReq.setUrl(QUrl(HTTP_URL + apiPath));
    httpReq.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-protobuf");

    // 2.发送请求(异步接口)
    QNetworkReply* httpResp = httpClient.post(httpReq, body);

    return httpResp;
}

template<typename T>
std::shared_ptr<T> network::NetClient::httpResponseHandle(QNetworkReply* httpResp, bool* ok, QString* reason)
{
    if(httpResp->error() != QNetworkReply::NoError) {
        *ok = false;
        *reason = httpResp->errorString();
        httpResp->deleteLater();
        return std::shared_ptr<T>();
    }
    QByteArray respBody = httpResp->readAll();  // 读取响应内容

    std::shared_ptr<T> respObj = std::make_shared<T>();
    respObj->deserialize(&serializer, respBody); // 反序列化

    if(!respObj->success()) {
        // 业务处理时返回错误
        *ok = false;
        *reason = httpResp->errorString();
        httpResp->deleteLater();
        return std::shared_ptr<T>();
    }
    *ok = true;
    httpResp->deleteLater();
    return respObj;
}

QString network::NetClient::makeRequestId()
{
    return "R" + QUuid::createUuid().toString().sliced(25, 12);
}

/* ===========================================
 * websocket
 * ========================================== */

void network::NetClient::initWebsocket()
{
    connect(&websocketClient, &QWebSocket::connected, this, [=](){
        LOG(LL_DEBUG, "connected");
        sendAuthentication();
    });;

    connect(&websocketClient, &QWebSocket::disconnected, this, [=](){
        LOG(LL_DEBUG, "disconnected");
    });

    connect(&websocketClient, &QWebSocket::errorOccurred, this, [=](QAbstractSocket::SocketError error){
        LOG(LL_ERROR, "error: {}", error);
    });

    connect(&websocketClient, &QWebSocket::textMessageReceived, this, [=](const QString& message){
        LOG(LL_DEBUG, "websocket text receive: {}, len: {}", message, message.length());
    });

    connect(&websocketClient, &QWebSocket::binaryMessageReceived, this, [=](const QByteArray& message){
        im_proto::NotifyMessage notifyMessage;
        notifyMessage.deserialize(&serializer, message);
        WsResponseHandle(notifyMessage);
        LOG(LL_DEBUG, "websocket binary receive: {}, len: {}", message, message.length());
    });

    websocketClient.open(WEBSOCKET_URL);
}

void network::NetClient::WsResponseHandle(const im_proto::NotifyMessage& notifyMessage)
{
    if(notifyMessage.notifyType() == im_proto::NotifyTypeGadget::NotifyType::CHAT_MESSAGE_NOTIFY) {
        // 收到消息
        model::Message msg;
        if(!notifyMessage.hasNewMessageInfo()) return;
        msg.load(notifyMessage.newMessageInfo().messageInfo());
        LOG(LL_DEBUG, "WsResponseHandle:: {}", msg.content);
        WsMessageHandle(msg);
    } else if(notifyMessage.notifyType() == im_proto::NotifyTypeGadget::NotifyType::CHAT_SESSION_CREATE_NOTIFY) {
        model::ChatSessionInfo chatSessionInfo;
        if(!notifyMessage.hasNewChatSessionInfo()) return;
        chatSessionInfo.load(notifyMessage.newChatSessionInfo().chatSessionInfo());
        WsSessionCreateHandle(chatSessionInfo);
    } else if(notifyMessage.notifyType() == im_proto::NotifyTypeGadget::NotifyType::FRIEND_ADD_APPLY_NOTIFY) {
        // 收到好友申请通知
        model::UserInfo userInfo;
        if(!notifyMessage.hasFriendAddApply()) return;
        userInfo.load(notifyMessage.friendAddApply().userInfo());
        WsFriendAddApplyHandle(userInfo);
    } else if(notifyMessage.notifyType() == im_proto::NotifyTypeGadget::NotifyType::FRIEND_ADD_PROCESS_NOTIFY) {
        // 好友申请处理结果通知
        model::UserInfo userInfo;
        if(!notifyMessage.hasFriendProcessResult()) return;
        userInfo.load(notifyMessage.friendProcessResult().userInfo());
        WsFriendProcessHandle(userInfo, notifyMessage.friendProcessResult().agree());
    } else if(notifyMessage.notifyType() == im_proto::NotifyTypeGadget::NotifyType::FRIEND_REMOVE_NOTIFY) {
        // 被好友删除通知
        WsRemoveFriendHandle(notifyMessage.friendRemove().userId());
    }
}

void network::NetClient::WsMessageHandle(const model::Message& message)
{
    QList<model::Message>* messageList = dataCenter->getRecentMessageList(message.charSessionId);
    if(messageList) {
        // 该会话已经加载到客户端本地了
        messageList->emplace_back(message);
        this->receiveMessage(message.charSessionId);
    } else {
        connect(dataCenter, &model::DataCenter::getRecentMessageListDoneNoUi, this, &NetClient::receiveMessage, Qt::UniqueConnection);
        dataCenter->getRecentMessageListAsync(message.charSessionId, false);
    }
}

void network::NetClient::WsSessionCreateHandle(const model::ChatSessionInfo& chatSessionInfo)
{
    dataCenter->addChatSession(chatSessionInfo);
    emit dataCenter->receiveSessionCreateDone();
}

void network::NetClient::WsRemoveFriendHandle(const QString& userId)
{
    dataCenter->removeFriend(userId);
    emit dataCenter->deleteFriendDone();
}

void network::NetClient::WsFriendAddApplyHandle(const model::UserInfo &userInfo)
{
    // 1. 添加到数据中心
    QList<model::UserInfo>* applyList =  dataCenter->getApplyList();
    if(!applyList) {
        LOG(LL_ERROR, "客户端没有加载好友申请列表");
        return;
    }
    applyList->emplaceFront(userInfo);

    // 2. 更新界面
    emit dataCenter->receiveFriendApplyDone();
}

void network::NetClient::WsFriendProcessHandle(const model::UserInfo& userInfo, bool agree)
{
    if(agree) {
        QList<model::UserInfo>* friendList = dataCenter->getFriendList();
        if(!friendList) {
            LOG(LL_ERROR, "客户端没有加载好友列表");
            return;
        }
        friendList->emplace_back(userInfo);
        emit dataCenter->receiveFriendProcessDone(userInfo.nickname, agree);
    } else {
        emit dataCenter->receiveFriendProcessDone(userInfo.nickname, agree);
    }
}

void network::NetClient::receiveMessage(const QString& chatSessionId)
{
    // 消息属于当前选中会话, 同步消息展示区 && 会话列表, 否则只同步会话列表状态
    if(chatSessionId == dataCenter->getCurrentChatSessionId()) {
        const model::Message& lastMessage = dataCenter->getRecentMessageList(chatSessionId)->back();
        // 信号同步更新消息展示区
        emit dataCenter->receiveMessageDone(lastMessage);
    } else {
        dataCenter->addUnread(chatSessionId);
    }

    // 发送信号, 更新会话列表的消息预览状态
    emit dataCenter->updateLastMessage(chatSessionId);
}



///////////////////// 让编译器为特定类型生成实例
template std::shared_ptr<im_proto::GetUserInfoRsp>
network::NetClient::httpResponseHandle<im_proto::GetUserInfoRsp>(QNetworkReply*, bool*, QString*);

template std::shared_ptr<im_proto::GetFriendListRsp>
network::NetClient::httpResponseHandle<im_proto::GetFriendListRsp>(QNetworkReply*, bool*, QString*);

template std::shared_ptr<im_proto::GetChatSessionListRsp>
network::NetClient::httpResponseHandle<im_proto::GetChatSessionListRsp>(QNetworkReply*, bool*, QString*);

template std::shared_ptr<im_proto::GetPendingFriendEventListRsp>
network::NetClient::httpResponseHandle<im_proto::GetPendingFriendEventListRsp>(QNetworkReply*, bool*, QString*);

template std::shared_ptr<im_proto::NewMessageRsp>
network::NetClient::httpResponseHandle<im_proto::NewMessageRsp>(QNetworkReply*, bool*, QString*);

template std::shared_ptr<im_proto::SetUserNicknameRsp>
network::NetClient::httpResponseHandle<im_proto::SetUserNicknameRsp>(QNetworkReply*, bool*, QString*);

template std::shared_ptr<im_proto::SetUserDescriptionRsp>
network::NetClient::httpResponseHandle<im_proto::SetUserDescriptionRsp>(QNetworkReply*, bool*, QString*);

template std::shared_ptr<im_proto::SetUserPhoneNumberRsp>
network::NetClient::httpResponseHandle<im_proto::SetUserPhoneNumberRsp>(QNetworkReply*, bool*, QString*);

template std::shared_ptr<im_proto::PhoneVerifyCodeRsp>
network::NetClient::httpResponseHandle<im_proto::PhoneVerifyCodeRsp>(QNetworkReply*, bool*, QString*);

template std::shared_ptr<im_proto::SetUserAvatarRsp>
network::NetClient::httpResponseHandle<im_proto::SetUserAvatarRsp>(QNetworkReply*, bool*, QString*);

template std::shared_ptr<im_proto::FriendRemoveRsp>
network::NetClient::httpResponseHandle<im_proto::FriendRemoveRsp>(QNetworkReply*, bool*, QString*);

template std::shared_ptr<im_proto::FriendAddRsp>
network::NetClient::httpResponseHandle<im_proto::FriendAddRsp>(QNetworkReply*, bool*, QString*);

template std::shared_ptr<im_proto::FriendAddProcessRsp>
network::NetClient::httpResponseHandle<im_proto::FriendAddProcessRsp>(QNetworkReply*, bool*, QString*);

template std::shared_ptr<im_proto::ChatSessionCreateRsp>
network::NetClient::httpResponseHandle<im_proto::ChatSessionCreateRsp>(QNetworkReply*, bool*, QString*);

template std::shared_ptr<im_proto::GetChatSessionMemberRsp>
network::NetClient::httpResponseHandle<im_proto::GetChatSessionMemberRsp>(QNetworkReply*, bool*, QString*);

template std::shared_ptr<im_proto::FriendSearchRsp>
network::NetClient::httpResponseHandle<im_proto::FriendSearchRsp>(QNetworkReply*, bool*, QString*);

template std::shared_ptr<im_proto::MsgSearchRsp>
network::NetClient::httpResponseHandle<im_proto::MsgSearchRsp>(QNetworkReply*, bool*, QString*);

template std::shared_ptr<im_proto::GetHistoryMsgRsp>
network::NetClient::httpResponseHandle<im_proto::GetHistoryMsgRsp>(QNetworkReply*, bool*, QString*);

template std::shared_ptr<im_proto::AccountLoginRsp>
network::NetClient::httpResponseHandle<im_proto::AccountLoginRsp>(QNetworkReply*, bool*, QString*);

template std::shared_ptr<im_proto::PhoneLoginRsp>
network::NetClient::httpResponseHandle<im_proto::PhoneLoginRsp>(QNetworkReply*, bool*, QString*);

template std::shared_ptr<im_proto::UserRegisterRsp>
network::NetClient::httpResponseHandle<im_proto::UserRegisterRsp>(QNetworkReply*, bool*, QString*);

template std::shared_ptr<im_proto::GetSingleFileRsp>
network::NetClient::httpResponseHandle<im_proto::GetSingleFileRsp>(QNetworkReply*, bool*, QString*);

template std::shared_ptr<im_proto::SpeechRecognitionRsp>
network::NetClient::httpResponseHandle<im_proto::SpeechRecognitionRsp>(QNetworkReply*, bool*, QString*);


